/*******************************************************************************************
 *      @name   sem_B.c
 *      @brief  这是在linux系统下创建信号量集的代码案例
 *      @date   2025/02/13
 *      @author cecelmx@126.com 
 *      @note
 *          1.linux系统下的IPC对象(共享内存段、消息队列、信号量组)都需要键值key，要通过ftok()生成
 *          2.创建的IPC对象是具有持久性的，属于系统的资源，进程的终止不影响的创建的IPC对象
 *          3.需要使用semget()函数来创建信号量集，通过指定IPC_CREAT来创建,并且要指定创建的权限0666
 *         
 * 
 *      @version  xx.xx.xx   主版本号.次版本号.修正版本号
 * 
 *      CopyRight (c)   2024-2025   Your Name     All Right Reserved
 * 
 * ****************************************************************************************/
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <string.h>
#include <sys/sem.h>


#define SEM_NUM  1      //用于表示信号量集中信号量的数量  

#define SHM_SIZE  128   //用于表示共享内存段的大小


int main()
{
    //1.生成键值key
    key_t sem_key  = ftok("./",0x87);
    if(sem_key == -1)
    {
        fprintf(stderr,"sem_key error,%s\n",strerror(errno));
        exit(-1);
    }

    //2.创建信号量集,访问权限是具有读写权限 当信号量集不存在时，可以创建，存在时会获取信号量集的ID
    int semid = semget(sem_key, SEM_NUM ,IPC_CREAT | IPC_EXCL | 0666);
    if(-1 == semid)
    {   
        //说明信号量集已经存在
        if(errno == EEXIST)
        {
            //要访问的信号量集是已经存在的，此时只需要获取信号量集的ID
            semid = semget(sem_key, SEM_NUM ,0);
            if(semid == -1)
            {
                fprintf(stderr,"semget error,%s\n",strerror(errno));
                exit(-1);
            }
        }
        else
        {
            fprintf(stderr,"semget error,%s\n",strerror(errno));
            exit(-1);
        }
       
    }
    
    printf("semget OK,sem key = %#x,sem id = %d\n",sem_key,semid);

    


    //2.申请共享内存段，并映射到自己的进程空间中
    
    key_t shm_key  = ftok("./",0x12);
    if(shm_key == -1)
    {
        fprintf(stderr,"shm_key error,%s\n",strerror(errno));
        exit(-1);
    }

    //2.创建共享内存,访问权限是具有读写权限 当共享内存段不存在时，可以创建，存在时会获取共享内存段的ID
    int shm_id = shmget(shm_key, SHM_SIZE ,IPC_CREAT | IPC_EXCL | 0666);
    if(-1 == shm_id)
    {   
        //说明共享内存段已经存在
        if(errno == EEXIST)
        {
            //要访问的共享内存段是已经存在的，此时只需要获取共享内存段的ID
            shm_id = shmget(shm_key, SHM_SIZE ,0);
            if(shm_id == -1)
            {
                fprintf(stderr,"shmget error,%s\n",strerror(errno));
                exit(-1);
            }
        }
        else
        {
            fprintf(stderr,"shmget error,%s\n",strerror(errno));
            exit(-1);
        }
       
    } 
    
    printf("shmget OK,shm key = %#x,shm id = %d\n",shm_key,shm_id);

    //3.把共享内存段映射到进程中
    char *addr = (char *)shmat(shm_id,NULL,0);

  
    //5.打算实现进程的同步，要求进程A先向共享内存段写入数据，然后进程B才去共享内存段中读取数据
    while(1)
    {
        //P操作，申请信号量
        struct sembuf psem = {0,-1,0};
        semop(semid,&psem,1);

        printf("read data from shm is %s\n",addr);

        //V操作，释放信号量
        struct sembuf vsem = {0,1,0};
        semop(semid,&vsem,1);
    }

    return 0;
}

